/******************************************************************************* * Signavio Core Components * Copyright (C) 2012 Signavio GmbH * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. ******************************************************************************/ package org.oryxeditor.server.diagram; import org.json.JSONException; import org.json.JSONObject; /** * Represents a rectangular space within a canvas. * Defined by the upper left and lower right points * * @author Philipp Maschke * */ public class Bounds { Point lowerRight; Point upperLeft; /** * Creates a bounds with 0,0 and 0,0 coordinates. */ public Bounds(){ this(new Point(0.0, 0.0), new Point(0.0, 0.0)); } /** * Creates the bounds with a json like structured string. * @param string */ public Bounds(String string){ try { JSONObject points = new JSONObject(string); this.upperLeft = new Point(points.getJSONObject("a")); this.lowerRight = new Point(points.getJSONObject("b")); validatePoints(); } catch (JSONException e) { this.lowerRight = new Point(0.0, 0.0); this.upperLeft = new Point(0.0, 0.0); } } /** * Creates the bounds with the json representation of it. * @param points */ public Bounds(JSONObject points){ try { this.upperLeft = new Point(points.getJSONObject("a")); this.lowerRight = new Point(points.getJSONObject("b")); validatePoints(); } catch (JSONException e) { this.lowerRight = new Point(0.0, 0.0); this.upperLeft = new Point(0.0, 0.0); } } /** * Constructs a Bounds defined by the two given points.<p/> * Constructs the rectangle which is spanned by the two points and then computes coordinates for upper left and lower right points.<br/> * This means, that the given points can be any two opposing corner points of the desired bounds * (they don't have to be upper left and lower right and they don't have to be in any order) * @param first a corner point of the desired bounds * @param second the opposing corner point of the desired bounds */ public Bounds(Point first, Point second) { this.upperLeft = first; this.lowerRight = second; validatePoints(); } /** * Returns a copy of the lower right point */ public Point getLowerRight() { return new Point(lowerRight); } // /** // * Sets the new lower right. Also checks whether the new coordinates are valid // * (upper left coordinates really in upper left; lower right coordinates really in lower right). // * <p/> // * <b>CAUTION:</b> Automatically swaps coordinates, such that these constraints hold! // * @param lowerRight the lowerRight to set // */ // public void setLowerRight(Point lowerRight) { // this.lowerRight = lowerRight; // validatePoints(); // } /** * Returns a copy of the upper left point */ public Point getUpperLeft() { return new Point(upperLeft); } // /** // * Sets the new upper left. Also checks whether the new coordinates are valid // * (upper left coordinates really in upper left; lower right coordinates really in lower right). // * <p/> // * <b>CAUTION:</b> Automatically swaps coordinates, such that these constraints hold! // * // * @param upperLeft the upperLeft to set // */ // public void setUpperLeft(Point upperLeft) { // this.upperLeft = upperLeft; // validatePoints(); // } /** * Updated the bounds with the new values. <p/> * Computes the rectangle spanned by the two given points and infers the new coordinates of upper left and lower right points.<bt/> * This means that the given points don't need to be in the "right" order and may even be the lower left and upper right coordinates. * * @param first one corner point of the new bounds * @param second corner point on the opposite side of first */ public void setCoordinates(Point first, Point second){ this.upperLeft = first; this.lowerRight = second; validatePoints(); } /** * Updated the bounds with the new values. <p/> * Computes the rectangle spanned by the two given points and infers the new coordinates of upper left and lower right points.<bt/> * This means that the given points don't need to be in the "right" order and may even be the lower left and upper right coordinates. * * @param firstX X-coordinate of one corner point of the new bounds * @param firstY Y-coordinate of one corner point of the new bounds * @param secondX X-coordinate of the corner point on the opposite side of first * @param secondY Y-coordinate of the corner point on the opposite side of first */ public void setCoordinates(Number firstX, Number firstY, Number secondX, Number secondY){ setCoordinates(new Point(firstX, firstY), new Point(secondX, secondY)); } /** * Move the bounds to the absolute point. * @param a */ public void moveTo(Point a){ Point size = new Point(this.getWidth(), this.getHeight()); this.upperLeft.set(a); this.lowerRight.set(a); this.lowerRight.add(size); } /** * Move the bounds relative. * @param a */ public void moveBy(Point a){ this.lowerRight.moveBy(a); this.upperLeft.moveBy(a); } /** * Extend the lowerRight with the coordinates * @param x * @param y */ public void extend(double x, double y){ this.lowerRight.add(x, y); } /** * Extend the lowerRight with the point * @param point */ public void extend(Point point){ this.extend(point.getX(), point.getY()); } /** * Widen the bounds with the upperleft and lowerright * @param x * @param y */ public void widen(double x, double y){ this.upperLeft.subtract(x, y); this.lowerRight.add(x, y); } /** * Widen the bounds with the upperleft and lowerright * @param point */ public void widen(Point point){ this.widen(point.getX(), point.getY()); } /** * Returns the middle point, the point between upperleft and lowerright * @return */ public Point getMiddle(){ Point p = this.getLowerRight(); p.subtract(this.getUpperLeft()); p.divide(2); return p; } /** * Return the center of the bounds * @return */ public Point getCenter(){ Point p = this.getMiddle(); p.add(this.upperLeft); return p; } /** * Returns a copy of the upper right point */ public Point getUpperRight(){ return new Point(getLowerRight().getX(), getUpperLeft().getY()); } /** * Returns a copy of the lower left point */ public Point getLowerLeft(){ return new Point(getUpperLeft().getX(), getLowerRight().getY()); } /** * Return the width * @return */ public double getWidth(){ return this.getLowerRight().getX() - this.getUpperLeft().getX(); } /** * Return the height * @return */ public double getHeight(){ return this.getLowerRight().getY() - this.getUpperLeft().getY(); } /** * Returns width and height of the bounds as a Point object * @return */ public Point getSize(){ return new Point(getWidth(), getHeight()); } /** * Return true if the coordinates is on or inside the bounds * @param x * @param y * @return */ public boolean isPointIncluded(double x, double y){ return this.upperLeft.getX() <= x && this.upperLeft.getY() <= y && this.lowerRight.getX() >= x && this.lowerRight.getY() >= y; } /** * Return true if the point is on or inside the bounds * @param point * @return */ public boolean isPointIncluded(Point point){ if (point == null) return false; return this.isPointIncluded(point.getX(), point.getY()); } /** * Returns a new instance of the given bounds */ public Bounds copy(){ return new Bounds(this.upperLeft.copy(), this.lowerRight.copy()); } /** * Returns a json representation of the bounds. * @return * @throws JSONException */ public JSONObject toJSON() throws JSONException{ JSONObject pos = new JSONObject(); pos.put("a", this.getUpperLeft().toJSON()); pos.put("b", this.getLowerRight().toJSON()); return pos; } /** * Returns a string representation of the bounds in * a json like structure. * In case of a JSONException, it returns super.toString(). * @return */ public String toString(){ try { return this.toJSON().toString(); } catch (JSONException e) { return super.toString(); } } /** * Checks whether lower right and upper left of both bounds are the same, using {@link Point#hasSameCoordinatesAs(Point)} * @param other * @return true, if lower right and upper left of both bounds are the same */ public boolean hasSamePositionsAs(Bounds other){ return upperLeft.hasSameCoordinatesAs(other.getUpperLeft()) && lowerRight.hasSameCoordinatesAs(other.getLowerRight()); } /** * Checks whether lower right and upper left of both bounds are the same, using {@link Point#hasSameCoordinatesAs(Point, double)} * @param other * @param maxDelta the maximum allowed difference per coordinate * @return true, if lower right and upper left of both bounds are the same (allowing for the given delta) */ public boolean hasSamePositionsAs(Bounds other, double maxDelta){ return upperLeft.hasSameCoordinatesAs(other.getUpperLeft(), maxDelta) && lowerRight.hasSameCoordinatesAs(other.getLowerRight(), maxDelta); } /** * Computes the actual rectangle spanned by the two given points and determines the actual upper left and lower right points. */ private void validatePoints(){ Point newLowerRight = new Point(Math.max(this.upperLeft.getX(), this.lowerRight.getX()), Math.max(this.upperLeft.getY(), this.lowerRight.getY())); Point newUpperLeft = new Point(Math.min(this.upperLeft.getX(), this.lowerRight.getX()), Math.min(this.upperLeft.getY(), this.lowerRight.getY())); upperLeft = newUpperLeft; lowerRight = newLowerRight; } }